import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;

/**
 * @modulename Error Handler
 * @description This class checks an enumeration of an error reported by the
 *              parser and gives the error message associated with that error,
 *              and the next action to be taken that involves the error.
 *              <p>
 * @callsequence
 * @inparam (int) The enumeration of the error.
 * @outparam (string) This is the message given by the error
 * @errorstested See JUnit test cases
 * @errorsgenerated See errata report
 * @author Jamie Trefzger
 * @createdate 10/13/09
 * @modlog <p>
 *         <ul>
 *         Who/When/Why
 *         <li>Travis 10/14/09 test javadoc
 *         </ul>
 *         Coding Standards Met: "Signed Code Leader(Jaron)"
 *         <p>
 *         Testing Standards Met: "Signed Test Leader(Jamie)"
 */
public class ErrorHandler {

	private static ArrayList<String> errorMessageArray;
	private static ArrayList<String> levelArray;
	private static ArrayList<String> codeArray;
	private static ArrayList<String> numberArray;

	private static ArrayList<String> errorListArray;
	private static ArrayList<String> lineNumberArray;
	private static ArrayList<String> customFieldArray;

	public static void startErrorHandler() {
		errorMessageArray = new ArrayList<String>(0);
		levelArray = new ArrayList<String>(0);
		codeArray = new ArrayList<String>(0);
		numberArray = new ArrayList<String>(0);

		errorListArray = new ArrayList<String>(0);
		lineNumberArray = new ArrayList<String>(0);
		customFieldArray = new ArrayList<String>(0);
		setUp();
	}

	/**
	 * @modulename ErrorHandler (constructor)
	 * 
	 * @description ErrorHandler is the default method that builds the Error
	 *              Table
	 * 
	 * @callsequence ErrorHandler(File fileName)
	 * 
	 * @inparam fileName The name of the file that you want to make the Error
	 *          table out of. Must be in the format of 3 columns, with headers
	 *          'Code','Level', and 'Error Message'. Each column must be
	 *          separated by a tab.
	 * @outparam none
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public ErrorHandler(File fileName) {
		try {
			BufferedReader ErrorHandler = new BufferedReader(new FileReader(
					fileName));
			String errorHandlerString = new String();
			String tempString = new String();
			while ((tempString = ErrorHandler.readLine()) != null) {
				errorHandlerString = errorHandlerString + tempString;
			}
			ErrorHandler.close();
			Scanner scanOb1 = new Scanner(errorHandlerString)
					.useDelimiter("\\s*\t\\s*");
			// without newlines, we have a string that used to be 4 columns,
			// each column separated by a tab.
			// this means that if we tab deliminate we can separate the columns
			// into their respective arrays.
			while (scanOb1.hasNext()) {
				tempString = scanOb1.next();
				numberArray.add(tempString);
				tempString = scanOb1.next();
				codeArray.add(tempString);
				tempString = scanOb1.next();
				levelArray.add(tempString);
				tempString = scanOb1.next();
				errorMessageArray.add(tempString);
			}
		} catch (IOException error) {
			System.err.println("Error: " + error);
		}
	}

	private static void setUp() {
		File errorTable = new File("src" + Main.systemPathSeparator
				+ "ErrorTable.txt");
		String errorHandlerString = FileHandler.readFile(errorTable);
		String tempString = new String();
		Scanner scanOb1 = new Scanner(errorHandlerString)
				.useDelimiter("\\s*\t\\s*");
		// without newlines, we have a string that used to be 4 columns, each
		// column separated by a tab.
		// this means that if we tab deliminate we can separate the columns into
		// their respective arrays.
		while (scanOb1.hasNext()) {
			tempString = scanOb1.next();
			numberArray.add(tempString);
			tempString = scanOb1.next();
			codeArray.add(tempString);
			tempString = scanOb1.next();
			levelArray.add(tempString);
			tempString = scanOb1.next();
			errorMessageArray.add(tempString);
		}
	}

	/**
	 * @modulename returnLevelForCode
	 * 
	 * @description Returns the error severity level for the given error code.
	 * 
	 * @callsequence returnLevelForCode(String code)
	 * 
	 * @inparam code The error code that we want to find the corresponding error
	 *          severity level of.
	 * @outparam (String) The severity level of the given error code.
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnLevelForCode(String code) {
		String tempString = new String();
		tempString = code.toUpperCase();
		int i1 = 0;
		i1 = codeArray.indexOf(tempString);
		tempString = levelArray.get(i1);
		return tempString;
	}

	/**
	 * @modulename returnMessageForCode
	 * 
	 * @description Returns the error message for the given error code.
	 * 
	 * @callsequence returnMessageForCode(String code)
	 * 
	 * @inparam code The error code that we want to find the corresponding error
	 *          message for.
	 * 
	 * @outparam (String) The error message of the given error code.
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnMessageForCode(String code) {
		String tempString = new String();
		tempString = code.toUpperCase();
		int i1 = 0;
		i1 = codeArray.indexOf(tempString);
		tempString = errorMessageArray.get(i1);
		return tempString;
	}

	/**
	 * @modulename returnNumberForCode
	 * 
	 * @description Returns the error reference number for the given error code.
	 * 
	 * @callsequence returnNumberForCode(String code)
	 * 
	 * @inparam code The code of the error you want to get the reference number
	 *          of.
	 * 
	 * @outparam (String) The reference number of the given error code.
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnNumberForCode(String code) {
		String tempString = new String();
		tempString = code.toUpperCase();
		int i1 = 0;
		i1 = codeArray.indexOf(tempString);
		tempString = numberArray.get(i1);
		return tempString;
	}

	/**
	 * @modulename returnCodeForNumber
	 * 
	 * @description Returns the error code for the given error reference number.
	 * 
	 * @callsequence returnCodeForNumber(String errorNumber)
	 * 
	 * @inparam errorNumber The number of the error code you want to get the
	 *          code for.
	 * @outparam (String) The error code of the given reference number.
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnCodeForNumber(String errorNumber) {
		return codeArray.get(numberArray.indexOf(errorNumber));
	}

	/**
	 * @modulename returnLevelForNumber
	 * 
	 * @description Returns the error level for the given error reference
	 *              number.
	 * 
	 * @callsequence returnLevelForNumber(String errorNumber)
	 * 
	 * @inparam errorNumber The number of the error code you want to get the
	 *          code level for.
	 * @outparam (String) The error level of the given reference number.
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static String returnLevelForNumber(String errorNumber) {
		return levelArray.get(numberArray.indexOf(errorNumber));
	}

	/**
	 * @modulename returnOutputString
	 * 
	 * @description returns the output string for an error number
	 * 
	 * @callsequence returnOutputString(String errorNumber)
	 * 
	 * @inparam errorNumber The number of the error code you want to get the
	 *          output string for
	 * 
	 * @outparam (String) the output string for the given error number
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static String returnOutputString(String errorNumber) {
		String tempString = new String();
		tempString = errorNumber + " ";
		tempString = tempString
				+ codeArray.get(numberArray.indexOf(errorNumber)) + " (";
		tempString = tempString
				+ levelArray.get(numberArray.indexOf(errorNumber)) + ") ";
		tempString = tempString
				+ errorMessageArray.get(numberArray.indexOf(errorNumber));
		System.out.println(tempString);
		return tempString;
	}

	/**
	 * @modulename addToErrorList
	 * 
	 * @description adds an error to the error list
	 * 
	 * @callsequence addToErrorList(String lineNumber, String errorNumber,
	 *               String customMessage)
	 * 
	 * @inparam (String) lineNumber: the line number that has the error,
	 *          (String) errorNumber: the error number for the error being
	 *          added, (String) customMessage: a custom message describing why
	 *          the error was reported
	 * 
	 * @outparam none
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void addToErrorList(String lineNumber, String errorNumber,
			String customMessage) {
		// adds an error to the list, and the line number in a corresponding
		// array.
		// the idea is we can build a list of errors as we find them
		// TODO check for these errors
		// lineNumber must be a number in the range of 0-4096
		// errorNumber must be between 1 and the highest number in the error
		// table.
		lineNumberArray.add(lineNumber);
		errorListArray.add(errorNumber);
		customFieldArray.add(customMessage);

	}

	/**
	 * @modulename addToErrorList
	 * 
	 * @description adds an error to the error list
	 * 
	 * @callsequence addToErrorList(String lineNumber, String errorNumber,
	 *               String customMessage)
	 * 
	 * @inparam (String) lineNumber: the line number that has the error,
	 *          (String) errorNumber: the error number for the error being added
	 * 
	 * @outparam none
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static void addToErrorList(String lineNumber, String errorNumber) {
		// adds an error to the list, and the line number in a corresponding
		// array.
		// the idea is we can build a list of errors as we find them
		// TODO check for these errors
		// lineNumber must be a number in the range of 0-4096
		// errorNumber must be between 1 and the highest number in the error
		// table.
		lineNumberArray.add(lineNumber);
		errorListArray.add(errorNumber);
		customFieldArray.add(null);
	}

	/**
	 * @modulename removeFirstError()
	 * 
	 * @description removes the first error from the error arrays
	 * 
	 * @callsequence removeFirstError();
	 * 
	 * @inparam none
	 * 
	 * @outparam (String) the first error represented as a string
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static String removeFirstError() {
		// the idea behind this is that once we output an error, we will no
		// longer
		// be interested in what it was. Thus, we can remove them as we output
		// them
		String tempString = new String();
		String errorNumber = new String();
		String lineNumber = new String();

		// make sure there is an item to remove before we try to remove
		// something.
		if (lineNumberArray.size() > 0) {
			lineNumber = lineNumberArray.remove(0);
			errorNumber = errorListArray.remove(0);
			tempString = "Line#=" + lineNumber + " ";
			tempString = "Error#=" + errorNumber + " ";
			tempString = tempString
					+ codeArray.get(numberArray.indexOf(errorNumber)) + " (";
			tempString = tempString
					+ levelArray.get(numberArray.indexOf(errorNumber)) + ") ";
			// if we did not pass a custom message, return the default message
			// in the table
			if (customFieldArray.size() != 0) {
				if (customFieldArray.get(0) == null) {
					tempString = tempString
							+ errorMessageArray.get(numberArray
									.indexOf(errorNumber));
					customFieldArray.remove(0);
				}
				// if we did pass a custom message, substitute that in for the
				// default message.
				else {
					tempString = tempString + customFieldArray.remove(0);
				}
			}
			System.out.println(tempString);
			return tempString;
		} else {
			return "No remaining errors to remove from the list.";
		}
	}

	/**
	 * @modulename removeError
	 * 
	 * @description removes an error from the error arrays
	 * 
	 * @callsequence removeError(int lineNumber)
	 * 
	 * @inparam (int) lineNumber: the line number that has the error to be
	 *          removed
	 * 
	 * @outparam (String) the first error represented as a string
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static String removeError(int lineNumber) {
		String lineNumberString = String.valueOf(lineNumber);
		String errorString = "";
		if (lineNumberArray.contains(lineNumberString)) {
			int index = lineNumberArray.indexOf(lineNumberString);
			lineNumberString = lineNumberArray.remove(index);
			String errorNumber = errorListArray.remove(index);
			String errorMessage = errorMessageArray.get(numberArray
					.indexOf(errorNumber));
			if (index < customFieldArray.size()) {
				errorMessage = customFieldArray.remove(index);
				if(errorMessage == null){
					errorMessage = errorMessageArray.remove(index);
				}
			}
			StringBuilder tempString = new StringBuilder();
			tempString.append("Line#=" + lineNumber + " ");
			tempString.append("Error#=" + errorNumber + " ");
			tempString.append(codeArray.get(numberArray.indexOf(errorNumber))
					+ " (");
			tempString.append(levelArray.get(numberArray.indexOf(errorNumber))
					+ ") ");
			tempString.append(errorMessage);
			errorString = tempString.toString();
		}
		System.out.println(errorString);
		return errorString;
	}

	/**
	 * @modulename returnErrorListSize()
	 * 
	 * @description returns the number of errors in the error arrays
	 * 
	 * @callsequence returnErrorListSize()
	 * 
	 * @inparam none
	 * 
	 * @outparam (int) the number of errors in the error list arrays
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static int returnErrorListSize() {
		// check to see if an element exists before removing it
		return lineNumberArray.size();
	}

	/**
	 * @modulename lineHasErrorIntermediateError
	 * 
	 * @description returns whether or not a line of code has an intermediate
	 *              error
	 * 
	 * @callsequence lineHasErrorIntermediateError(int lineNumber)
	 * 
	 * @inparam (int) lineNumber: the line number of source code being checked
	 * 
	 * @outparam (boolean) =true if line has and intermediate error and =false
	 *           if not
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static boolean lineHasErrorIntermediateError(int lineNumber) {
		String lineNumberString = String.valueOf(lineNumber);
		if (lineNumberArray.contains(lineNumberString)) {
			int index = lineNumberArray.indexOf(lineNumberString);
			String errorNumber = errorListArray.get(index);
			String level = returnLevelForNumber(errorNumber);
			if (level.equalsIgnoreCase("Intermediate")) {
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}

	/**
	 * @modulename lineHasErrorError
	 * 
	 * @description returns whether or not a line of code has anerror
	 * 
	 * @callsequence lineHasErrorError(int lineNumber)
	 * 
	 * @inparam (int) lineNumber: the line number of source code being checked
	 * 
	 * @outparam (boolean) =true if line has and error and =false if not
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */
	public static boolean lineHasError(int lineNumber) {
		String lineNumberString = String.valueOf(lineNumber);
		if (lineNumberArray.contains(lineNumberString)) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @modulename currentLineNumber()
	 * 
	 * @description returns the current line number
	 * 
	 * @callsequence currentLineNumber()
	 * 
	 * @inparam none
	 * 
	 * @outparam (int) the current line number
	 * 
	 * @errorstested See JUnit test cases
	 * @errorsgenerated See errata report
	 * @author Jamie Trefzger
	 * @createdate 10/13/09
	 * 
	 * @modlog Who/When/Why
	 * 
	 *         Coding Standards Met: "Signed Code Leader(Jaron)"
	 * 
	 *         Testing Standards Met: "Signed Test Leader(Jamie)"
	 */

	public static String currentLineNumber() {
		// allows us to check and see if the first element is equal
		// to the current line we are outputting. if it isn't, we
		// can move on to the next line in main.
		if (lineNumberArray.size() != 0) {
			return lineNumberArray.get(0);
		} else {
			return "0";
		}
	}
}